GameEng = new Class({
	
	Implements : [Options],
	
	/**
	 * Main container for map/game board
	 * @var Element
	 */
	map : null,
	
	/**
	 * Main container for user/battle console
	 * @var Console
	 */
	console : null,

	/**
	 * Container for displaying unit/tile info
	 * @var TileInfo
	 */
	tileInfo : null,
	
	/**
	 * @var Request.Queue
	 */
	requestQueue : null,

	/**
	 * ID of player whose turn it currently is
	 * @var int
	 */
	activePlayer : 1,
	
	/**
	 * Which player will command their own turn and watch everyone elses
	 * @var int
	 */
	hostPlayer : 1,
	
	/**
	 * Players in game
	 * @var Array
	 */
	players : new Array(),
	
	options : {
		
	},
	
	private : {
		initResLog : null,
		selected : null
	},
	
	/**
	 * Engine Constructor
	 */
	initialize : function(options){
		this.setOptions(options);
	},
	
	/**
	 * Begin Game!
	 */
	begin : function(){
		var obj = this;
		this.initUserInterface();
		this.loadResources();
		var startingInf = this.initConnection();
		startingInf.log.addEvent('complete',function(){
			if(startingInf.response.players && startingInf.response.players.length){
				for(var n=0;n<startingInf.response.players.length;n++){
					obj.addPlayer(startingInf.response.players[n]);
				}
			}
			obj.loadUnits.delay(500,obj,[startingInf.response.units]);
		});
		this.initControls();
		
		
	},
	
	/**
	 * Connect to game and get game info
	 */
	initConnection : function(){
		this.requestQueue = new Request.Queue();
		var startingInf = new GameCommand(command.beginGame);
		startingInf.dispatch('connectToGame');
		this.checkForUpdates.periodical(updatePollTime,this);
		return startingInf;
	},
	
	/**
	 * Load units onto map
	 * @var json units
	 */
	loadUnits : function(units){
		for(var n=0;n<units.length;n++){
			var params = units[n];
			var shipTest = new Unit(units[n].id,params);
			shipTest.setInvisible();
			this.map.addUnit(shipTest, units[n].x, units[n].y, units[n].r);
		}
		this.map.scanBattlefield();
	},
	
	/**
	 * Attach keyboard control events
	 */
	initControls : function(){
		var obj = this;
		window.addEvent('keypress',function(e){
			// demo stuff
			Unit.getUnitById('1').fire();
			if(obj.private.selected && !obj.console.input.getProperty('isFocused')){
				var move = {
					'unit'		: obj.getSelected(),
					'direction' : null,
					'preLog'	: null,
					'preAction'	: 'move',
					'request'	: new Hash({'c':'GameEng','a':'moveUnit'})
				};
				var ctrlKey = (e.ctrlKey===true || e.control===true) ? true : false;
				switch(true){
					case (ctrlKey && e.code==97): // CTRL + num pad 8 - North
					case (ctrlKey && e.code==35):
						var unit = obj.getSelected();
						if(unit && unit._type=="Unit"){
							var c = unit.getCoordinates();
							obj.map.burstRecursive(c.x,c.y,"SW",Map.selectNearestUnit);
						}
					break;
					case (e.code==97): // num pad 1
					case (e.code==35):
						move.direction = 'sw';
						var cmd = new GameCommand(null,move);
						cmd.dispatch();
						break;
					
					case (ctrlKey && e.code==98): // CTRL + num pad 2 - South
					case (ctrlKey && e.code==40):
						var unit = obj.getSelected();
						if(unit && unit._type=="Unit"){
							var c = unit.getCoordinates();
							obj.map.burstRecursive(c.x,c.y,"S",Map.selectNearestUnit);
						}
						break;
					case (e.code==98): // num pad 2
					case (e.code==40):
						move.direction = 's';
						var cmd = new GameCommand(null,move);
						cmd.dispatch();
						break;
						
					case (ctrlKey && e.code==99): // CTRL + num pad 3 - South East
					case (ctrlKey && e.code==34):
						var unit = obj.getSelected();
						if(unit && unit._type=="Unit"){
							var c = unit.getCoordinates();
							obj.map.burstRecursive(c.x,c.y,"SE",Map.selectNearestUnit);
						}
						break;
					case (e.code==99): // num pad 3
					case (e.code==34):
						move.direction = 'se';
						var cmd = new GameCommand(null,move);
						cmd.dispatch();
						break;
					
					case (ctrlKey && e.code==100): // CTRL + num pad 4 - West
					case (ctrlKey && e.code==37):
						var unit = obj.getSelected();
						if(unit && unit._type=="Unit"){
							var c = unit.getCoordinates();
							obj.map.burstRecursive(c.x,c.y,"W",Map.selectNearestUnit);
						}
						break;
					case (e.code==100): // num pad 4
					case (e.code==37):
						move.direction = 'w';
						var cmd = new GameCommand(null,move);
						cmd.dispatch();
						break;
						
					case (e.code==101): // num pad 5
					case (e.code==12):
						// @todo
						break;
					
					case (ctrlKey && e.code==102): // CTRL + num pad 6 - East
					case (ctrlKey && e.code==39):
						var unit = obj.getSelected();
						if(unit && unit._type=="Unit"){
							var c = unit.getCoordinates();
							obj.map.burstRecursive(c.x,c.y,"E",Map.selectNearestUnit);
						}
						break;
					case (e.code==102): // num pad 6
					case (e.code==39):
						move.direction = 'e';
						var cmd = new GameCommand(null,move);
						cmd.dispatch();
						break;
					
					case (ctrlKey && e.code==103): // CTRL + num pad 7 - North West
					case (ctrlKey && e.code==36):
						var unit = obj.getSelected();
						if(unit && unit._type=="Unit"){
							var c = unit.getCoordinates();
							obj.map.burstRecursive(c.x,c.y,"NW",Map.selectNearestUnit);
						}
						break;
					case (e.code==103): // num pad 7
					case (e.code==36):
						move.direction = 'nw';
						var cmd = new GameCommand(null,move);
						cmd.dispatch();
						break;
						
					case (ctrlKey && e.code==104): // CTRL + num pad 8 - North
					case (ctrlKey && e.code==38):
						var unit = obj.getSelected();
						if(unit && unit._type=="Unit"){
							var c = unit.getCoordinates();
							obj.map.burstRecursive(c.x,c.y,"N",Map.selectNearestUnit);
						}
						break;
					case (e.code==104): // num pad 8 - North
					case (e.code==38):
						move.direction = 'n';
						var cmd = new GameCommand(null,move);
						cmd.dispatch();
						break;
						
					case (ctrlKey && e.code==105): // CTRL + num pad 9 - North East
					case (ctrlKey && e.code==33):
						var unit = obj.getSelected();
						if(unit && unit._type=="Unit"){
							var c = unit.getCoordinates();
							obj.map.burstRecursive(c.x,c.y,"NE",Map.selectNearestUnit);
						}
						break;
					case (e.code==105): // num pad 9
					case (e.code==33):
						move.direction = 'ne';
						var cmd = new GameCommand(null,move);
						cmd.dispatch();
						break;
				}
				e.stop();
			}
		});
	},
	
	/**
	 * Initialise User Interface
	 */
	initUserInterface : function(){
		this.console = new Console(this.options);
		this.map 	 = new Map(this.options,this.console);
		this.tileInfo= new TileInfo($('tileInfo'));
	},
	
	/**
	 * Download Game art/resources.. is this required? would allow a download bar
	 */
	loadResources : function(){
		//var initResLog = this.console.logCommand('Initialising resources...');
		// @todo
		//initResLog.append(' successful.');
	},
	
	/**
	 * Used to track which unit is selected at any one time
	 * 
	 * @param Unit unit
	 */
	setSelected : function(unit){
		this.private.selected = unit; 
	},
	
	/**
	 * Return currently selected unit
	 * 
	 * @return Unit|null
	 */
	getSelected : function(){
		return this.private.selected;
	},
	
	/**
	 * Clear any currently selected units
	 */
	clearSelected : function(){
		this.private.selected = null;
	},
	
	/**
	 * Has the host player reached the end of his turn
	 * @return boolean
	 */
	isTurnEnd : function(){
		var units = g.map.unitsFlat;
		for(n=0;n<units.length;n++){
			if(units[n].getPlayer()===this.activePlayer && units[n].getActionPointsLeft()){
				return false;
			}
		}
		return true;
	},
	
	switchTurn : function(){
		this.activePlayer = 2; // @todo unhardcode
		if(this.activePlayer!=this.hostPlayer){
			alert("switch turn");
		}
	},
	
	/**
	 * Call server to check for game updates
	 */
	checkForUpdates : function(){
		var obj = this;
		var req = new Request.JSON({
			'url' : 'g.php?c=GameEng&a=checkForUpdates&rnd='+(Math.random()*100000).round(),
			'onSuccess' : function(resp){
				if(resp.moves && resp.moves.length){
					for(var n=0;n<resp.moves.length;n++){
						var move = resp.moves[n];
						var unit = Unit.getUnitById(move.id);
						if(unit) unit.move(move.x,move.y,move.r,true,false);
					}
				}
				if(resp.chat && resp.chat.length){
					for(var n=0;n<resp.chat.length;n++){
						var msg = resp.chat[n].msg;
						var player = Player.getPlayerById(resp.chat[n].p);
						if(msg && player) obj.console.addChatMsg(player,msg); 
					}
				}
				delete resp;
			}
		});
		this.requestQueue.addRequest(req.url,req);
		this.requestQueue.send(req.url);
	},
	
	/**
	 * Add player to game
	 * @var array player
	 */
	addPlayer : function(player){
		this.players.push(new Player(player));
	}
});

/********* GameEng Static Methods *********/

/**
 * Get Game Engine Instance (Singleton)
 */
GameEng.getInstance = function(){
	if(GameEng._instance){
		return GameEng._instance;
	}else{
		GameEng._instance = new GameEng();
		return GameEng._instance;
	}
}